Retrofit2 是 Square 公司推出的支持 Java 和 Android 平台的 Http 开源框架。该框架使用注解的方式构建 Http 请求,想法非常巧妙,API 也设计的非常优雅。
本文基于 Retrofit2.0.2
写在前面
发送请求的一般过程,以官网代码为例。
1 | //接口定义 |
1 | Retrofit retrofit = new Retrofit.Builder() |
1 | //发送异步请求 |
开始之前,有些概念需要提前说明一下。
- Service Method 指什么?
接口定义称之为 Service Method。之所以这么说,是因为接口的定义信息都被解析为了一个叫ServiceMethod
类型的对象。后面会详细解释。 - Return Type 与 Response Type?
responseType
是指接口定义中的泛型部分,即之前例子中的List<Repo>
。而 Return type 是指Call<List<Repo>>
。
先来看Retrofit.create(class<T> service)
方法,这就是魔法发生的地方。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override
public Object invoke(Object proxy, Method method, Object... args){
// 一些必要的判断
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
Platform
用于支持多个平台,包括 Java Android 和 iOS(需要额外的支持包)。同时,Platform
包含了默认的回调 Executor(之后会详细介绍) 。
可以看到,create(...)
方法使用代理方式 Proxy.newProxyInstance
为每个 Service Method 创建代理对象。这也解释了为什么我们没有实现 Service Method,却可以发送请求。
下面详细介绍 Retrofit2 处理 HTTP 请求的过程的各个阶段。
解析接口定义,构造 ServiceMethod 对象
ServiceMethod
是 Retrofit2 中重要的对象,包含了构造 HTTP 请求的所有信息。1
2
3
4
5
6
7
8
9
10
11ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
loadServiceMethod
方法非常简单,只是根据接口定义构造 SeiviceMethod
并进行 缓存。下面我们着重看下如何构造 SeiviceMethod
对象。SeiviceMethod
类使用Builder 模式进行创建。该类中只有两个 public 方法:SeiviceMethod.Builder 构造函数和 SeiviceMethod.Builder.build() 方法。
构造函数只是简单的赋值语句,下面详细介绍 build()
方法。
1 | public ServiceMethod build() { |
排除掉错误校验部分,该方法包含了四个部分:
- 处理
CallAdapter
,顺便解析responseType
。 - 处理
responseConverter
,用于将返回数据解析为responseType
类型。 - 处理方法注解,解析请求方式、编码格式等方法注解。
- 处理参数注解,根据参数类型和注解为每一个参数创建相应的
ParameterHandler
。
为什么是这四个部分?
我们再看一下接口的定义。
1
2 @GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);接口定义一共包含:方法注解、返回值Call、泛型类型 List
>、参数列表(参数注解和参数类型)。ServeiceMethod 就是为了解析接口方法,也就对应了接口的四个部分。
下面依次探索这四个部分都是如何处理的。
处理 CallAdapter
什么是 CallAdapter
CallAdapter
包含两个方法:
Type responseType()
返回该 CallAdapter 处理的 response 类型,该类型是指Call<List<Repo>>
中的 List。 <R> T adapt(Call<R> call)
将Call
类型转换为 T 类型,不能处理的类型返回 null 。
CallAdapter.Factory
根据接口定义的返回值(Call>)创建
CallAdapter
实例。
自定义 CallAdapter 需要继承自 CallAdapter.Factory。一般情况下不需要自定义。
已支持的 CallAdapter
- RxJava
- Java8
- Guava
CallAdapter 从哪来
1 | // 删除部分校验代码 |
1 | public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations){ |
1 | public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { |
以上代码可以看出,通过遍历 Retrofit
中的 adapterFactories
找到第一个可以处理该返回值的 CallAdapter
。
当接口返回 Call<?> 时,不需要设置 CallAdapter。此时,是如何处理的?
Retrofit.Builder.build() 方法中,adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor))
添加了默认的CallAdapter
。对于 Android 平台而言,默认的 Executor 是Platform.Android.MainThreadExecutor
,默认的 CallAdapter.Factory 是ExecutorCallAdapterFactory
。
处理 Response Converter
注意,此处的 Converter 是指用于解析返回数据的 Response Converter。之后还有用于处理 Service Method 参数的 Converter。
什么是 Converter
Converter
只有一个方法 T convert(F value) throws IOException
。用于将 Http 请求中的 请求参数 和 返回数据 转换为相应的类型。
自定义Converter
需要继承Converter.Factory
。Factory
共有三个方法:
Converter<ResponseBody, ?> responseBodyConverter(...)
用于将返回数据转换为相应类型,不能处理返回 null 。Converter<?, RequestBody> requestBodyConverter(...)
用于将请求数据中使用Body
,Part
,PartMap
注解的参数转换为RequestBody
。Converter<?, String> stringConverter(...)
用于将请求数据中使用Field
,FieldMap
,Header
,Path
,Query
,QueryMap
注解的参数转换为Stirng
。
BuildInConverters
返回数据转换
仅支持ResponseBody
和Void
类型。其中,转换类型为ResponseBody
时,根据是否存在方法注解Streaming
,返回 非 Buffer 的ResponseBody
和 Buffer 的ResponseBody
。Body
,Part
,PartMap
注解转换
仅支持RequestBody
->RequestBody
类型。Field
,FieldMap
,Header
,Path
,Query
,QueryMap
注解转换仅支持String
->String
类型。
已支持的 Converter
- 用于将
String
,基本数据类型及其包装类转换为text/plain
的ScalarsConverterFactory
。 - 使用
Gson
的GsonConverterFactory
。 - 使用
Jackson
的JacksonConverterFactory
。 - 使用
Moshi
的MoshiConverterFactory
。 - 使用
Protocol Buffer
的ProtoConverterFactory
。 - 使用
Simple
解析 xml 的SimpleXmlConverterFactory
。 - 使用
Wire
解析 Protocol Buffer 的WireConverterFactory
。
Response Converter 从哪来
Converter
处理与 CallAdapter
类似。遍历 Retrofit.converterFactories
寻找第一个能够处理该 response 类型的 Converter
。
为什么使用 json converter,如 Gson、Jackson 时,需要最后添加该 Converter?
对于 Json 数格式而言,GsonConverter 无法判断是否可以处理该 Response 。responseBodyConverter() 方法的返回值永远不为 null。如果 GsonConverter 不是最后添加的话,其他的 Converter 永远没有机会处理该 Response,哪怕 GsonConverter 不能处理该 Response 。
处理方法注解
方法注解包括 Http 请求方式、请求地址、请求头。
处理参数注解
按照参数顺序,根据参数注解,为每个参数构造相应的 ParameterHandler
,保存在数组 parameterHandlers
当中。ParameterHandler
用于将处理接口参数,并将参数添加到到请求相应部分当中。
参数注解有且只能有一个。
- @Url
注解使用ParameterHandler.RelativeUrl
实例进行处理,且参数类型必须为okhttp3.HttpUrl
,String
,java.net.URI
,android.net.Uri
其中之一。 - @Path
注解使用ParameterHandler.Path
实例进行处理。具体来说,首先在Retrofit.converterFactories
中寻找能够处理该参数类型的 Converter ,即Converter.Facyory.stringConverter(...)
方法返回值不为 null 。如果未找到相应的 Converter,就使用BuiltInConverters.ToStringConverter
,仅调用参数的toString()
方法。 - @Query
- @QueryMap
- @Header
- @Field
- @FieldMap
- @Part
- @PartMap
- @Body
发送请求,解析数据,回调
ServiceMethod
对象包含了构造 Http 请求的所有信息,但是这些信息是如何使用的呢?让我们重新回到 Retrofit.create()
方法。1
2OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
使用 ServiceMethod
和 Service Mehtod 参数构造 OkHttpCall。OkHttpCall 就是 Retrofit2 中发送网络请求的对象。但是,Retrofit2 的网络请求部分是由 okhttp3.Call 处理的。
构造 OkHttpCall,创建 okhttp3.Call
1 | private okhttp3.Call createRawCall() throws IOException { |
okhttp3.Call 的创建分为两步:构造 okhttp3.Request 、构造 okhttp3.Call 。
okhttp3.Request 转换为 okhttp3.Call 的过程由 okhttp3.Call.Factory ,即 OkHttpClient 完成,不再继续深究。下面详细看下如何生成 okhttp3.Request 。1
2
3
4
5
6
7
8
9
10
11
12
13Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
ServiceMethod 使用 RequestBuilder 将之前已经解析过的接口信息组装为 Request。
对 OkHttpCall 进行适配
我们已经知道 CallAdapter 的作用是将 Call(OkHttpCall) 对象转换为其他类型。
Android 平台下,默认的转换后的类型为 ExecutorCallAdapterFactory.ExecutorCallbackCall
。ExecutorCallbackCall
构造函数 ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate)
。其内部方法除了 enqueue(...)
方法,其他全部直接调用 delegate
方法。着重看一下 enqueue(...)
方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29@Override
public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
delegate.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
@Override
public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
ExecutorCallbackCall.enqueue
使用 callbackExecutor
执行 callback
。所以说,callback
执行线程为 Android 主线程。
CallbackExecutor 只对返回值为 Call<?> 的 Service Method 有作用,对自定义返回值类型无效。
发送请求
根据以上分析可知,Call<List<Repo>> call = service.listRepos("octocat")
方法返回值实际为 ExecutorCallbackCall
对象。当执行 call.enqueue
时,调用 OkHttpCall.enqueue(...)
方法,进一步调用 okhttp.Call.enqueue(new okhttp3.Callback())
发送网络请求。
解析返回数据
OkHttpCall.parseResponse()
方法负责返回数据的解析。此方法内部调用 ServiceMethod.toResponse(...)
解析返回数据。
总结
Retrofit2 网络请求处理分为四个阶段:
- 解析 ServiceMethod 构造
ServiceMethod
对象。这是 Retrofit2 最复杂的部分。
① 解析Http请求相关信息,如:请求方式、头信息、接口地址,请求体编码格式。
③ 寻找能够处理返回类型的 Converter。
② 寻找能够处理参数类型的 Converter。
④ 寻找能够处理 Service Method 返回值的 CallAdapter。 - 使用 ServiceMethod 构造 okhttp3.Call,并发送请求。
- 使用
ServiceMethod.responseConverter
解析返回数据 - 进行回调
收获
Bulider 模式与 Factory 模式的异同
- 都用于简化对象创建过程,提高代码的可读性。
- Builder 模式需要了解对象构建的细节,适用于可选参数过多的对象创建。
- Factory 模式不需要了解对象的创建细节,仅需要对象创建的“特征描述”。
以Retrofit
和Converter
的创建为例。
创建Retrofit
对象,需要 url,CallAdapter 等多个可选对象。如果直接使用 new 方法创建的话,参数列表较多,而且可能出现多个参数为 null 的情况,大大影响代码的可读性。相反,使用 Builder 模式,可以采用链式调用,选择性地传递需要的参数,可以保持代码的简单和优雅。
对于Converter
对象,使用者不需要了解其创建细节,只需要告诉 Factory ,自己需要什么类型的 Converter 就行。如,Factory.requestBodyConverter()
表明一个可以将某种类型转换为RequestBody
的转换器。
Java 类型处理
retrofit2.Utils 中有大量的工具方法用于处理类型信息。以根据 Method Service 返回类型获取 Response type 为例。1
2
3
4
5
6
7static Type getCallResponseType(Type returnType) {
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
return getParameterUpperBound(0, (ParameterizedType) returnType);
}
1 | static Type getParameterUpperBound(int index, ParameterizedType type) { |
代码写的很清楚,似乎也不需要解释,( >﹏< ),这些 API 倒是长见识。